package cfgutil

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"

	"gopkg.in/ini.v1"
)

var configFiles = make(map[string]*ini.File)

// defaultIni 使用默認配置項加載ini配置文件，配置的路徑必須為相對路徑config，配置文件的名稱必須為server.ini
func defaultIni() {

	// 获取可执行文件的绝对路径
	execPath, err := os.Executable()
	if err != nil {
		fmt.Printf("获取可执行文件路径失败: %v \r\n", err)
		panic("获取可执行文件路径失败，無法加載配置文件: " + err.Error())
	}

	configPath := filepath.Join(filepath.Dir(execPath), "config/server.ini")

	configFile, err := ini.Load(configPath)
	if err != nil {
		fmt.Printf("加载配置文件失败: %v \r\n", err)
		panic("加載配置文件失敗：" + err.Error())
	}

	configFiles["server"] = configFile
}

// createIni 使用指定的配置文件路径和文件名创建ini配置文件
func createIni(iniPath string, fileName string) {

	var configPath string
	if filepath.IsAbs(iniPath) {
		configPath = filepath.Join(iniPath, fileName+".ini")
	} else {
		execPath, err := os.Executable()
		if err != nil {
			fmt.Printf("获取可执行文件路径失败: %v \r\n", err)
			panic("获取可执行文件路径失败，無法加載配置文件: " + err.Error())
		}

		configPath = filepath.Join(filepath.Dir(execPath), iniPath, fileName+".ini")
	}

	cfg, err := ini.Load(configPath)
	if err != nil {
		fmt.Printf("加载配置文件失败: %v \r\n", err)
		panic("加載配置文件失敗：" + err.Error())
	}

	configFiles[fileName] = cfg
}

// iniSection 返回指定名称的配置节
func iniSection(name string) IniCfgSection {
	return iniSectionWithProfile("server", name)
}

func iniSectionWithProfile(profile string, name string) IniCfgSection {
	return IniCfgSection{
		Section: configFiles[profile].Section(name),
	}
}

func bindIniToMap[T any](sectionName string) map[string]T {
	return bindIniWithProfileToMap[T]("server", sectionName)
}

// bindIniToMap 绑定ini配置節到map，配置節的名稱必須包含一個變量值作爲map的key
//
//	如 test-item-a , test-item-b 對應到 map[string]T
//	生成后的格式為
//	map[a]={test-item-a}, map[b]={test-item-a}
func bindIniWithProfileToMap[T any](profile string, sectionName string) map[string]T {
	var tMap = make(map[string]T)

	var dummy T
	objType := reflect.TypeOf(dummy)

	if objType.Kind() == reflect.Ptr {
		objType = objType.Elem()
	}

	if objType.Kind() != reflect.Struct {
		panic("T must be a struct type or pointer to struct type")
	}

	prefix := strings.ReplaceAll(sectionName, "{key}", "")

	for _, section := range configFiles[profile].Sections() {
		key := strings.ReplaceAll(section.Name(), prefix, "")
		if strings.HasPrefix(section.Name(), prefix) {
			// 绑定配置值到对象
			item := bindIniWithProfileToObj[T](profile, section.Name())
			tMap[key] = item
		}
	}

	return tMap
}

func bindIniToObj[T any](sectionName string) T {
	return bindIniWithProfileToObj[T]("server", sectionName)
}

// bindIniWithProfileToObj 绑定ini配置節到对象,對象的字段屬性需要包含對應的tag，如果沒有設置，則根據屬性名的大小寫轉換為xx.xx.xx
//
//	例如對象
//	type Test struct {
//	Name         string `cfg:"test.name"`
//	Age          int    `cfg:"test.age"`
//	TestPassword string
//	}
//	如果tag中帶有：分割的内容，則會直接指向對應節點的内容，例如
//	Age          int    `cfg:"server:test.age"`,會直接將server節下的test.age設置到Age中
func bindIniWithProfileToObj[T any](profile string, sectionName string) T {
	// 创建 T 类型的实例
	var dummy T
	objType := reflect.TypeOf(dummy)

	// 如果是结构体指针类型，获取指向的类型，并创建实例
	var newValue reflect.Value
	if objType.Kind() == reflect.Ptr {
		objType = objType.Elem()
		newValue = reflect.New(objType)
	} else {
		// 如果是值类型，创建指针以便后续绑定
		newValue = reflect.New(objType)
	}

	// 确保是结构体类型
	if objType.Kind() != reflect.Struct {
		panic("T must be a struct type or pointer to struct type")
	}

	// 获取配置section
	section := configFiles[profile].Section(sectionName)

	// 获取结构体值
	structValue := newValue.Elem()

	// 遍历字段并绑定配置
	for i := 0; i < objType.NumField(); i++ {
		field := objType.Field(i)
		fieldValue := structValue.Field(i)
		tag := field.Tag.Get("cfg")

		// 如果没有tag，使用驼峰转点分格式
		if tag == "" {
			tag = camelToDotNotation(field.Name)
		}

		// 检查字段是否可以设置
		if !fieldValue.CanSet() {
			continue
		}

		// 处理指针类型字段
		if fieldValue.Kind() == reflect.Ptr {
			// 如果指针是nil，需要创建一个新的实例
			if fieldValue.IsNil() {
				// 创建指针指向的新值
				newPtrValue := reflect.New(fieldValue.Type().Elem())
				fieldValue.Set(newPtrValue)
			}
			// 获取指针指向的值
			fieldValue = fieldValue.Elem()
		}

		var key *ini.Key
		if strings.Contains(tag, ":") {
			split := strings.Split(tag, ":")
			key = configFiles["server"].Section(split[0]).Key(split[1])
		} else {
			key = section.Key(tag)
		}

		// 根据字段类型设置值
		switch fieldValue.Kind() {
		case reflect.String:
			itemValue := key.String()
			fieldValue.SetString(itemValue)
		case reflect.Bool:
			itemValue := key.MustBool()
			fieldValue.SetBool(itemValue)
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			itemValue := key.MustInt64()
			if !fieldValue.OverflowInt(itemValue) {
				fieldValue.SetInt(itemValue)
			} else {
				fmt.Printf("警告: 字段 %s 的值 %d 溢出\n", field.Name, itemValue)
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			itemValue := key.MustUint64()
			if !fieldValue.OverflowUint(itemValue) {
				fieldValue.SetUint(itemValue)
			} else {
				fmt.Printf("警告: 字段 %s 的值 %d 溢出\n", field.Name, itemValue)
			}
		case reflect.Float32, reflect.Float64:
			itemValue := key.MustFloat64()
			if !fieldValue.OverflowFloat(itemValue) {
				fieldValue.SetFloat(itemValue)
			} else {
				fmt.Printf("警告: 字段 %s 的值 %f 溢出\n", field.Name, itemValue)
			}
		default:
			fmt.Printf("不支持的类型: %v\n", fieldValue.Kind())
		}
	}

	// 根据泛型类型返回相应值
	if reflect.TypeOf(dummy).Kind() == reflect.Ptr {
		return newValue.Interface().(T)
	} else {
		return newValue.Elem().Interface().(T)
	}
}

// camelToDotNotation 将驼峰命名转换为点分格式
func camelToDotNotation(camel string) string {
	if camel == "" {
		return ""
	}

	var result []rune
	for i, r := range camel {
		// 如果是大写字母且不是第一个字符，添加点号
		if i > 0 && r >= 'A' && r <= 'Z' {
			result = append(result, '.')
			result = append(result, runeToLower(r))
		} else {
			result = append(result, runeToLower(r))
		}
	}
	return string(result)
}

// runeToLower 将大写字母转换为小写
func runeToLower(r rune) rune {
	if r >= 'A' && r <= 'Z' {
		return r + ('a' - 'A')
	}
	return r
}

type IniCfgSection struct {
	Section *ini.Section
}

func (c IniCfgSection) String(name string) string {
	return c.Section.Key(name).String()
}
func (c IniCfgSection) Int(name string) int {
	return c.Section.Key(name).MustInt()
}
func (c IniCfgSection) Float(name string) float64 {
	return c.Section.Key(name).MustFloat64()
}
func (c IniCfgSection) Bool(name string) bool {
	return c.Section.Key(name).MustBool()
}
func (c IniCfgSection) Duration(name string) time.Duration {
	return c.Section.Key(name).MustDuration()
}
